#include "hk_camera.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <iostream>
#include <cstring> 
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <chrono>
#include <iomanip>


namespace hk {

// 裸数据文件读写锁
std::mutex rawDataMutex;
std::vector<uint8_t> raw_data;

// 图片队列读写锁
// std::mutex imageQueueMutex;
// std::queue<std::vector<uint8_t>> imageQueue;
/**
 * @brief 裸数据采集线程回调函数
 * @param pBuffer: 上报数据起始地址
 * @param dwBufSize: 数据长度（byte）
 */
void CALLBACK g_RealDataCallBack_V30(LONG lRealHandle, DWORD dwDataType, BYTE *pBuffer, DWORD dwBufSize, void* dwUser)
{
    //将获取到的全屏测温温度矩阵数据保存到本地文件，后面需要根据数据结构自行解析
    switch (dwDataType)
    {
    case NET_DVR_SYSHEAD:
        break;
    case NET_DVR_STREAMDATA:
        // if (DeviceFunction::fTemData != NULL)
        // {
        //     std::lock_guard<std::mutex> lock(fileMutex);
        //     fwrite(pBuffer, dwBufSize, 1, DeviceFunction::fTemData);
        // }
        {
            std::lock_guard<std::mutex> lock(rawDataMutex);
            // 将新的数据拷贝到raw_data
            raw_data.insert(raw_data.end(), pBuffer, pBuffer + dwBufSize);      
            // std::cout << "Size of raw_data: " << raw_data.size() << " bytes" << std::endl;
        }
        break;
    default:
        break;
    }
}

/**
 * @brief 析构函数:确保线程被正确结束
 * @param strucLoginInfo: 网络连接和登录信息
 * @param strucDeviceInfo: 登录后设备返回信息
 */
DeviceFunction::DeviceFunction()
    : lUserID(-1), lRealPlayHandle(-1), running_(false)  {
    // 初始化
    
    std::memset(&loginInfo, 0, sizeof(loginInfo));
    std::memset(&deviceInfo, 0, sizeof(deviceInfo));

    // 填充 loginInfo 和 deviceInfo 的字段
    std::strncpy(loginInfo.sDeviceAddress, "192.168.1.64", sizeof(loginInfo.sDeviceAddress) - 1);
    loginInfo.wPort = 8000;
    std::strncpy(loginInfo.sUserName, "admin", sizeof(loginInfo.sUserName) - 1);
    std::strncpy(loginInfo.sPassword, "JIQIREN1234", sizeof(loginInfo.sPassword) - 1);

    std::cout << "初始化信息:" << std::endl;
    std::cout << "IP:" << loginInfo.sDeviceAddress << std::endl;
    std::cout << "端口:" << loginInfo.wPort << std::endl;
    std::cout << "用户名:" << loginInfo.sUserName << std::endl;
    std::cout << "密码:" << loginInfo.sPassword << std::endl;
}

/**
 * @brief 判断网络相机连接状态
 * @param ipAddress: IP
 * @param port: 端口
 */
bool DeviceFunction::isConnect(const std::string& ipAddress, int port, int timeoutSec = 5) {
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) {
        std::cerr << "Error creating socket" << std::endl;
        return false;
    }

    struct sockaddr_in serv_addr;
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(port);
    if (inet_pton(AF_INET, ipAddress.c_str(), &serv_addr.sin_addr) <= 0) {
        std::cerr << "Invalid IP address" << std::endl;
        close(sockfd);
        return false;
    }

    // 设置超时时间
    struct timeval timeout;
    timeout.tv_sec = timeoutSec;
    timeout.tv_usec = 0;
    setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(timeout));
    setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, (const char*)&timeout, sizeof(timeout));

    int result = connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
    close(sockfd);

    return result == 0;
}

/**
 * @brief 线程处理函数：检查设备网络连接，实现：重连、登录、开取流（热插拔）
 */
void DeviceFunction::check_login_status_thread() {
    bool previous_connect_status = false;
    bool current_connect_status = false;
    while (running_) {
    // 执行线程任务：检测网络相机连接
        current_connect_status = isConnect(loginInfo.sDeviceAddress, loginInfo.wPort);
        if (current_connect_status == true && previous_connect_status == false) {
            NET_DVR_Init();
            std::cout << "摄像头已连接，重新登录中..." << std::endl;
            
            lUserID = NET_DVR_Login_V40(&loginInfo, &deviceInfo);
            if (lUserID < 0)
            {
                std::cout << "重新登录失败，继续尝试..." << std::endl;
                std::cerr << "NET_DVR_Login_V40, error code:" << NET_DVR_GetLastError() << std::endl;
                NET_DVR_Cleanup();
                previous_connect_status = false;
                continue;
            }
            // 离开作用域，自动解锁
            // 每次重新登录，都重新开启获取裸数据线程
            start_get_raw_data_thread();
            if (0 > lRealPlayHandle) {
                // 退出登录、释放资源
                NET_DVR_Logout(lUserID);
                NET_DVR_Cleanup();
                previous_connect_status = false;
                continue;
            }
            std::cout << "登录成功！" << std::endl;
        }
        else if (current_connect_status == false && previous_connect_status == false)
        {
            std::cout << "摄像头断开网络连接..." << std::endl;
            // 每次断开链接，则关闭获取裸数据线程
            NET_DVR_Cleanup();
            lUserID = -1;
            stop_get_raw_data_thread();
        }
        else {
            // std::cout << "摄像头连接正常" << std::endl;
        }
        previous_connect_status = current_connect_status;
        // 3秒检测一次
        std::this_thread::sleep_for(std::chrono::seconds(3));
    }
    stop_get_raw_data_thread();

    // 退出登录、释放资源
    NET_DVR_Logout(lUserID);
    NET_DVR_Cleanup();
    std::cout << "check_login_status_thread is stopping." << std::endl;
}

/**
 * @brief 启动登录状态线程
 */
void DeviceFunction::start_check_login_status_thread() {
    running_ = true;
    thread_ = std::thread(&DeviceFunction::check_login_status_thread, this);   //指定线程处理函数
}

/**
 * @brief 停止登录状态线程
 */
void DeviceFunction::stop_check_login_status_thread() {
    running_ = false;
    join();
}

/**
 * @brief 启动获取裸数据线程
 */
void DeviceFunction::start_get_raw_data_thread() {
    //全屏测温参数配置
    //输入参数
    NET_DVR_XML_CONFIG_INPUT struInput = { 0 };
    struInput.dwSize = sizeof(struInput);

    //输出参数
    NET_DVR_XML_CONFIG_OUTPUT struOutputParam = { 0 };
    struOutputParam.dwSize = sizeof(struOutputParam);

    char szUrl[512]; //URL
    char szOutput[8 * 1024] = { 0 }; //输出结果
    char szStatusBuf[1024] = { 0 };  //状态信息
    char pBuf[2 * 1024] = { 0 };

    //获取参数
    memset(szUrl, 0, sizeof(szUrl));
    sprintf(szUrl, "%s", "GET /ISAPI/Thermal/channels/1/streamParam\r\n");

    //不同功能对应不同URL
    struInput.lpRequestUrl = szUrl;
    struInput.dwRequestUrlLen = strlen(szUrl);

    //获取时输入为空
    struInput.lpInBuffer = NULL;
    struInput.dwInBufferSize = 0;

    //分配输出内存
    memset(szOutput, 0, sizeof(szOutput));
    struOutputParam.lpOutBuffer = szOutput;
    struOutputParam.dwOutBufferSize = sizeof(szOutput);

    //输出状态
    memset(szStatusBuf, 0, sizeof(szStatusBuf));
    struOutputParam.lpStatusBuffer = szStatusBuf;
    struOutputParam.dwStatusSize = sizeof(szStatusBuf);
   
    if (!NET_DVR_STDXMLConfig(lUserID, &struInput, &struOutputParam))
    {
        printf("NET_DVR_STDXMLConfig failed, error code: %d\n", NET_DVR_GetLastError());
    }
    else
    {
        printf("NET_DVR_STDXMLConfig successfully!\n");
        printf("\n%s", szOutput);
    }
    
    //设置参数
    memset(szUrl, 0, sizeof(szUrl));
    sprintf(szUrl, "%s", "PUT /ISAPI/Thermal/channels/1/streamParam\r\n");

    //输入JSON数据
    memset(pBuf, 0, sizeof(pBuf));
    strcpy(pBuf, "<ThermalStreamParam xmlns=\"http://www.isapi.org/ver20/XMLSchema\" version=\"2.0\">"
        "<videoCodingType>real-time_raw_data</videoCodingType>"
        "</ThermalStreamParam>");

    //输入参数
    struInput.lpInBuffer = pBuf;
    struInput.dwInBufferSize = sizeof(pBuf);

    //输出结果
    memset(szOutput, 0, sizeof(szOutput));
    struOutputParam.lpOutBuffer = szOutput;
    struOutputParam.dwOutBufferSize = sizeof(szOutput);

    //输出状态
    memset(szStatusBuf, 0, sizeof(szStatusBuf));
    struOutputParam.lpStatusBuffer = szStatusBuf;
    struOutputParam.dwStatusSize = sizeof(szStatusBuf);

    if (!NET_DVR_STDXMLConfig(lUserID, &struInput, &struOutputParam))
    {
        printf("NET_DVR_STDXMLConfig failed, error code: %d\n", NET_DVR_GetLastError());
    }
    else
    {
        printf("NET_DVR_STDXMLConfig successfully!\n");
        printf("lpOutBuffer: \n%s", szOutput);
        printf("lpStatusBuffer: \n%s", szStatusBuf);
    }
    

    //获取参数
    memset(szUrl, 0, sizeof(szUrl));
    sprintf(szUrl, "%s", "GET /ISAPI/Thermal/channels/1/thermometry/pixelToPixelParam\r\n");

    //不同功能对应不同URL
    struInput.lpRequestUrl = szUrl;
    struInput.dwRequestUrlLen = strlen(szUrl);

    //获取时输入为空
    struInput.lpInBuffer = NULL;
    struInput.dwInBufferSize = 0;

    //分配输出内存
    memset(szOutput, 0, sizeof(szOutput));
    struOutputParam.lpOutBuffer = szOutput;
    struOutputParam.dwOutBufferSize = sizeof(szOutput);

    //输出状态
    memset(szStatusBuf, 0, sizeof(szStatusBuf));
    struOutputParam.lpStatusBuffer = szStatusBuf;
    struOutputParam.dwStatusSize = sizeof(szStatusBuf);
    
    if (!NET_DVR_STDXMLConfig(lUserID, &struInput, &struOutputParam))
    {
        printf("NET_DVR_STDXMLConfig failed, error code: %d\n", NET_DVR_GetLastError());
    }
    else
    {
        printf("NET_DVR_STDXMLConfig successfully!\n");
        printf("\n%s", szOutput);
    }
    //设置参数
    memset(szUrl, 0, sizeof(szUrl));
    sprintf(szUrl, "%s", "PUT /ISAPI/Thermal/channels/1/thermometry/pixelToPixelParam\r\n");
    struInput.lpRequestUrl = szUrl;
    struInput.dwRequestUrlLen = strlen(szUrl);

    //输入JSON数据
    memset(pBuf, 0, sizeof(pBuf));
    //帧数*100即为maxFrameRate字段的值
    strcpy(pBuf, "<?xml version=\"1.0\" encoding=\"UTF - 8\"?>"
        "<PixelToPixelParam version = \"2.0\" xmlns = \"http://www.hikvision.com/ver20/XMLSchema\">"
        "<id>1</id>"
        "<maxFrameRate>400</maxFrameRate>"
        "<reflectiveEnable>false</reflectiveEnable>"
        "<reflectiveTemperature>20.00</reflectiveTemperature>"
        "<emissivity>0.98</emissivity>"
        "<distance>0.5</distance>"
        "<refreshInterval>50</refreshInterval>"
        "<distanceUnit>centimeter</distanceUnit>"
        "<temperatureDataLength>4</temperatureDataLength>"
        "<JpegPictureWithAppendData>"
        "<jpegPicEnabled>true</jpegPicEnabled>"
        "<visiblePicEnabled>true</visiblePicEnabled>"
        "</JpegPictureWithAppendData>"
        "</PixelToPixelParam>");
    struInput.lpInBuffer = pBuf;
    struInput.dwInBufferSize = sizeof(pBuf);

    //输出结果
    memset(szOutput, 0, sizeof(szOutput));
    struOutputParam.lpOutBuffer = szOutput;
    struOutputParam.dwOutBufferSize = sizeof(szOutput);

    //输出状态
    memset(szStatusBuf, 0, sizeof(szStatusBuf));
    struOutputParam.lpStatusBuffer = szStatusBuf;
    struOutputParam.dwStatusSize = sizeof(szStatusBuf);
    
    if (!NET_DVR_STDXMLConfig(lUserID, &struInput, &struOutputParam))
    {
        printf("NET_DVR_STDXMLConfig failed, error code: %d\n", NET_DVR_GetLastError());
    }
    else
    {
        printf("NET_DVR_STDXMLConfig successfully!\n");
        printf("lpOutBuffer: \n%s", szOutput);
        printf("lpStatusBuffer: \n%s", szStatusBuf);
    }

    NET_DVR_PREVIEWINFO struPlayInfo = { 0 };
    struPlayInfo.hPlayWnd = NULL;         //需要SDK解码时句柄设为有效值，仅取流不解码时可设为空
    struPlayInfo.lChannel = 1;       //预览通道号
    struPlayInfo.dwStreamType = 0;       //0-主码流，1-子码流，2-码流3，3-码流4，以此类推
    struPlayInfo.dwLinkMode = 0;       //0- TCP方式，1- UDP方式，2- 多播方式，3- RTP方式，4-RTP/RTSP，5-RSTP/HTTP
    struPlayInfo.byVideoCodingType = 1;  //码流数据编解码类型：0- 通用编码数据，1- 热成像数据
    struPlayInfo.bBlocked = 1;       //0- 非阻塞取流，1- 阻塞取流
    
    lRealPlayHandle = NET_DVR_RealPlay_V40(lUserID, &struPlayInfo, g_RealDataCallBack_V30, NULL);
    if (lRealPlayHandle < 0)
    {
        printf("NET_DVR_RealPlay_V40 error, %d\n", NET_DVR_GetLastError());
        std::cerr << "启动播放失败, 重试中..." << std::endl;
    }
}

/**
 * @brief 停止获取裸数据线程
 */
void DeviceFunction::stop_get_raw_data_thread() {
    std::cout << "get_raw_data_thread is stopping." << std::endl;
    
    NET_DVR_StopRealPlay(lRealPlayHandle);
    lRealPlayHandle = -1;      
    
    // if (fTemData != NULL)
    // { 
    //     fclose(fTemData);
    //     fTemData = NULL;
    // }
}

/**
 * @brief 等待线程结束
 */
void DeviceFunction::join() {
    if (thread_.joinable()) {
        thread_.join();
    }
}

/**
 * @brief 析构函数:确保线程被正确结束
 */
DeviceFunction::~DeviceFunction() {
    stop_check_login_status_thread(); // 请求线程停止
    std::cout << "DeviceFunction destructed." << std::endl;
}

/**
 * @brief 析构函数:确保线程被正确结束
 */
void DeviceFunction::focus_setup(int step) {
    NET_DVR_XML_CONFIG_INPUT struInput = { 0 };
    struInput.dwSize = sizeof(struInput);

    //输出参数
    NET_DVR_XML_CONFIG_OUTPUT struOutputParam = { 0 };
    struOutputParam.dwSize = sizeof(struOutputParam);

    char szUrl[512]; //URL
    char szOutput[8 * 1024] = { 0 }; //输出结果
    char szStatusBuf[1024] = { 0 };  //状态信息
    char pBuf[2 * 1024] = { 0 };
    //设置参数
    memset(szUrl, 0, sizeof(szUrl));
    sprintf(szUrl, "%s", "PUT /ISAPI/System/Video/inputs/channels/1/focus\r\n");
    struInput.lpRequestUrl = szUrl;
    struInput.dwRequestUrlLen = strlen(szUrl);
    //输入JSON数据
    memset(pBuf, 0, sizeof(pBuf));
    snprintf(pBuf, sizeof(pBuf), "<?xml version=\"1.0\" encoding=\"UTF-8\"?><FocusData><focus>%d</focus></FocusData>", step);
    // snprintf(pBuf, sizeof(pBuf), "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
    //                          "<FocusConfiguration version=\"2.0\" xmlns=\"http://www.isapi.org/ver20/XMLSchema\">\n"
    //                          "  <focusStyle>MANUAL</focusStyle>\n"
    //                          "  <focusSpeed>1</focusSpeed>\n"
    //                          "  <relativeFocusPos>%d</relativeFocusPos>\n"
    //                          "  <temperatureChangeAdaptEnabled>true</temperatureChangeAdaptEnabled>\n"
    //                          "  <highTemperaturePriority>false</highTemperaturePriority>\n"
    //                          "</FocusConfiguration>", step);
    struInput.lpInBuffer = pBuf;
    struInput.dwInBufferSize = sizeof(pBuf);
    //输出结果
    memset(szOutput, 0, sizeof(szOutput));
    struOutputParam.lpOutBuffer = szOutput;
    struOutputParam.dwOutBufferSize = sizeof(szOutput);

    //输出状态
    memset(szStatusBuf, 0, sizeof(szStatusBuf));
    struOutputParam.lpStatusBuffer = szStatusBuf;
    struOutputParam.dwStatusSize = sizeof(szStatusBuf);
    
    if (!NET_DVR_STDXMLConfig(lUserID, &struInput, &struOutputParam))
    {
        printf("focus_setup NET_DVR_STDXMLConfig failed, error code: %d\n", NET_DVR_GetLastError());
    }
    else
    {
        printf("focus_setup NET_DVR_STDXMLConfig successfully!\n");
        printf("lpOutBuffer: \n%s", szOutput);
        printf("lpStatusBuffer: \n%s", szStatusBuf);
    }
}

// ------------------------------------------------------------------------------------------------------

GetVideoStream::GetVideoStream() 
    : running_(false)  {
    std::cout<< "GetVideoStream created." << std::endl;
}

GetVideoStream::~GetVideoStream() {
    stop_get_video_stream_thread(); // 请求线程停止
    std::cout << "GetVideoStream destructed." << std::endl;
}

/**
 * @brief 查找裸数据缓存中，魔术字是否存在2个，并返回2个魔术字在缓存中的位置，否则返回{-1, -1}
 * @param raw_data: 裸数据缓存
 * @return positions[0], positions[1]: 前后2个魔术字的位置
 */
std::pair<int, int> GetVideoStream::findMagicBytesPositions(const std::vector<uint8_t>& raw_data) {
    int count = 0;
    int positions[2] = {-1, -1};
    const std::vector<uint8_t> MAGIC_BYTES = {0x73, 0x77, 0x82, 0x70};

    if (raw_data.size() < MAGIC_BYTES.size()) {
        return {-1, -1};
    }

    for (size_t i = 0; i <= raw_data.size() - MAGIC_BYTES.size(); ++i) {
        if (std::memcmp(&raw_data[i], MAGIC_BYTES.data(), MAGIC_BYTES.size()) == 0) {
            if (count < 2) {
                positions[count] = i;
                count++;
            }
            if (count == 2) {
                break;
            }
        }
    }

    if (count == 2) {
        return {positions[0], positions[1]};
    } else {
        return {-1, -1}; // 如果没有找到两个魔法字，返回负数
    }
}

/**
 * @brief 拷贝裸数据缓存中的图片数据并返回，删除缓存中已被拷贝过的数据
 * @param raw_data: 裸数据缓存
 * @return imageData：一帧图片缓存
 */
std::vector<uint8_t> GetVideoStream::processRawData(std::vector<uint8_t>& raw_data) {
    std::vector<uint8_t> imageData;
    std::lock_guard<std::mutex> lock(rawDataMutex);
    
    auto [firstMagicPos, secondMagicPos] = findMagicBytesPositions(raw_data);

    if (firstMagicPos == -1 || secondMagicPos == -1) {
        // std::cerr << "Could not find two magic bytes in raw_data." << std::endl;
        return imageData;
    }

    // 确保我们有足够的空间来提取数据
    size_t startPos = firstMagicPos + 132;
    size_t endPos = secondMagicPos;

    if (startPos >= raw_data.size() || endPos > raw_data.size() || startPos >= endPos) {
        std::cerr << "Invalid positions for extracting image data." << std::endl;
        return imageData;
    }

    // 提取图像数据
    imageData.insert(imageData.end(), raw_data.begin() + startPos, raw_data.begin() + endPos - (384*2*2));
    raw_data.erase(raw_data.begin() + firstMagicPos, raw_data.begin() + secondMagicPos);
    return imageData;
}

/**
 * @brief 提供访问图片队列接口，返回图片队列
 * @return newImage:图片
 */
std::vector<uint16_t> GetVideoStream::getImage() {
    if (imageQueue.empty()) {
        return {};
    }
    else{
        std::lock_guard<std::mutex> lock(mtx_newImageQueue);
        newImage = std::move(imageQueue.front());
        imageQueue.pop();
        return newImage;
    }
}

void GetVideoStream::start_get_video_stream_thread() {
    running_ = true;
    thread_ = std::thread(&GetVideoStream::get_video_stream_thread, this);   //指定线程处理函数
}

void GetVideoStream::stop_get_video_stream_thread() {
    std::cout << "stop_get_video_stream_thread is stopping." << std::endl;
    running_ = false;
    join();
}

void GetVideoStream::join() {
    if (thread_.joinable()) {
        thread_.join();
    }
}

/**
 * @brief 把处理好的一帧图片数据推进imageQueue队列中
 */
void GetVideoStream::get_video_stream_thread() {
    while (running_) {
        std::vector<uint8_t> imagebuffer = processRawData(raw_data);
        if (imagebuffer.size() == 221184) {
            // newImage = imagebuffer;
            size_t outputSize = 221184 / 2;
            std::vector<uint16_t> tempBuffer(outputSize);
            // 将每两个 uint8_t 字节合并为一个 uint16_t
            for (size_t i = 0; i < outputSize; ++i) {
                // 取出两个字节
                uint8_t lowByte = imagebuffer[2 * i];
                uint8_t highByte = imagebuffer[2 * i + 1];
                // 合并两个字节为一个 uint16_t
                tempBuffer[i] = static_cast<uint16_t>(highByte) << 8 | lowByte;
            }
            {
                // 加锁保护队列操作
                std::lock_guard<std::mutex> lock(mtx_newImageQueue);
                // 如果需要限制队列大小，可以在这里添加逻辑
                if (imageQueue.size() > maxQueueSize) {
                    // 丢弃数据或等待消费者处理
                    imageQueue.pop(); // 丢弃最早的数据
                }
                // 将数据压入队列
                imageQueue.push(std::move(tempBuffer));
            }
        }
        std::this_thread::sleep_for(std::chrono::microseconds(100));
    }
}
}